package syntaxAnalysis.core;

import java.util.HashSet;
import java.util.Set;

public class CFG {
	private Production[] productions;
	private final String startSymbol="E";

	public CFG(Production[] productions) {
		super();
		this.productions = productions;
	}

	/**
	 * this method accepts a production and returns set of first value;
	 * 
	 * @param p
	 *            a production
	 * @return set of first value
	 */
	public Set<String> first(Production p) {
		Set<String> vts = new HashSet<>();
		String right = p.getRight();
		if (p.isTerminal(right)) {
			vts.add(right);
		} else {
			String first = String.valueOf(right.charAt(0));
			if (p.isTerminal(first)) {
				vts.add(first);
			} else {
				for (Production pr : productions) {
					if (pr.getLeft().startsWith(first)) {
						Set<String> firtOfPr = first(pr);
						vts.addAll(firtOfPr);
					}
				}
			}
		}
		return vts;
	}

	/**
	 * This method accepts a Non-terminal symbol and returns its set of first
	 * 
	 * @param Nonterminal
	 * @return
	 */
	public Set<String> first(String nonTerminal) {
		Set<String> vts = new HashSet<>();
		for (Production p : productions) {
			if (p.getLeft().equals(nonTerminal)) {
				vts.addAll(first(p));
			}
		}
		return vts;

	}

	/**
	 * 
	 * @param p
	 * @return
	 */
	public Set<String> follow(Production p) {
		Set<String> vts = new HashSet<>();
		String left = p.getLeft();
		if (left.equals(startSymbol))
			vts.add("$");
		for (Production pr : productions) {
			String prRight = pr.getRight();
			if (prRight.contains(left)) {
				if (prRight.endsWith(left) && (!(pr.getLeft().equals(left)))) {
					vts.addAll(follow(pr));
				} else if (!prRight.endsWith(left)) {
					String nextOfPLeft = String.valueOf(prRight.charAt(prRight.indexOf(left) + 1));
					if (p.isTerminal(nextOfPLeft)) {
						vts.add(nextOfPLeft);
					} else {
						Set<String> firstOfNext = first(nextOfPLeft);
						if (firstOfNext.contains("0")) {
							firstOfNext.remove("0");
							vts.addAll(firstOfNext);
							vts.addAll(follow(pr.getLeft()));
						} else {
							vts.addAll(firstOfNext);
						}
					}
				}
			}
		}

		return vts;
	}

	public Set<String> follow(String nonTerminal) {
		Set<String> vts = new HashSet<>();
		for (Production p : productions) {
			if (p.getLeft().contains(nonTerminal)) {
				vts.addAll(follow(p));
				break;
			}
		}
		return vts;
	}

	public Production[] getProductions() {
		return productions;
	}

	public void setProductions(Production[] productions) {
		this.productions = productions;
	}

}
